import tkinter as tk
import tkinter.ttk as ttk
from sapi import *
from tip import TipTool
from puzzle import Puzzle
from grid import Grid
from color import *
from msglabel import MessageLabel
from drawingdialog import ChainDialog


class Tools:
    def __init__(self, master, puzzle: Puzzle):
        self.master = master
        self.puzzle: Puzzle = puzzle

        self.frame: tk.Frame = tk.Frame(master)
        self.frame.pack(side=tk.TOP, fill=tk.X)

        self.frame.columnconfigure(0, weight=1)
        self.frame.columnconfigure(1, weight=1)
        self.frame.columnconfigure(2, weight=1)

        newButton = tk.Button(self.frame, text="New", command=self.new)
        newButton.grid(column=0, row=0, sticky=tk.NSEW, padx=5, pady=2)

        cueButton = tk.Button(self.frame, text="Cue", command=self.cue)
        cueButton.grid(column=1, row=0, sticky=tk.NSEW, padx=5, pady=2)

        shuffleButton = tk.Button(
            self.frame, text="Shuffle", command=lambda: self.shuffle(openText)
        )
        shuffleButton.grid(column=2, row=0, sticky=tk.NSEW, padx=5, pady=2)

        imageButton = tk.Button(self.frame, text="Image", command=self.image)
        imageButton.grid(column=0, row=1, sticky=tk.NSEW, padx=5, pady=2)

        drawButton = tk.Button(self.frame, text="draw", command=self.open_draw_dialog)
        drawButton.grid(column=1, row=1, sticky=tk.NSEW, padx=5, pady=2)

        backButton = tk.Button(self.frame, text="Back", command=self.back)
        backButton.grid(column=0, row=2, sticky=tk.NSEW, padx=5, pady=2)

        checkButton = tk.Button(
            self.frame, text="Check", command=lambda: self.check(msgLabel)
        )
        checkButton.grid(column=1, row=2, sticky=tk.NSEW, padx=5, pady=2)

        solveButton = tk.Button(
            self.frame, text="Solve", command=lambda: self.solve(msgLabel)
        )
        solveButton.grid(column=2, row=2, sticky=tk.NSEW, padx=5, pady=2)

        tipButton = tk.Button(
            self.frame, text="Tip", command=lambda: self.tip(msgLabel)
        )
        tipButton.grid(column=0, row=3, sticky=tk.NSEW, padx=5, pady=2)

        self.selected_strategy = tk.StringVar()
        self.selected_strategy.set("Step")
        self.strategy_cb = ttk.Combobox(self.frame, textvariable=self.selected_strategy)
        self.strategy_cb["values"] = [
            "Step",
        ]
        self.strategy_cb.grid(
            column=1, row=3, sticky=tk.NSEW, padx=1, pady=1, columnspan=2
        )
        self.strategy_cb.bind("<<ComboboxSelected>>", lambda e: self.change_strategy())

        msgLabel = MessageLabel(self.frame)
        msgLabel.grid(column=0, row=4, columnspan=3, sticky=tk.NSEW, padx=5, pady=2)

        openText = tk.Text(self.frame, height=12)
        openText.grid(column=0, row=5, columnspan=3, sticky=tk.NSEW, padx=5, pady=2)

        inputButton = tk.Button(
            self.frame, text="Input", command=lambda: self.input(openText)
        )
        inputButton.grid(column=0, row=6, sticky=tk.NSEW, padx=5, pady=2)

        outputButton = tk.Button(
            self.frame, text="Output", command=lambda: self.output(openText)
        )
        outputButton.grid(column=1, row=6, sticky=tk.NSEW, padx=5, pady=2)

        encodeButton = tk.Button(
            self.frame, text="Encode", command=lambda: self.encode(openText)
        )
        encodeButton.grid(column=2, row=6, sticky=tk.NSEW, padx=5, pady=2)

        rotateButton = tk.Button(
            self.frame, text="Rotate", command=lambda: self.rotate(openText)
        )
        rotateButton.grid(column=0, row=7, sticky=tk.NSEW, padx=5, pady=2)

        # transposeButton = tk.Button(
        #    self.frame, text="Transpose", command=lambda: self.transpose(openText)
        # )
        # transposeButton.grid(column=1, row=7, sticky=tk.NSEW, padx=5, pady=2)

        upDownButton = tk.Button(
            self.frame, text="Up Down", command=lambda: self.upDown(openText)
        )
        upDownButton.grid(column=1, row=7, sticky=tk.NSEW, padx=5, pady=2)

        leftRightButton = tk.Button(
            self.frame, text="Left Right", command=lambda: self.leftRight(openText)
        )
        leftRightButton.grid(column=2, row=7, sticky=tk.NSEW, padx=5, pady=2)

        self.fetch_strategies()

    def back(self):
        self.puzzle.back()

    def change_strategy(self):
        print("select_strategy")
        self.puzzle.tipTool.tip_init()

    def check(self, msgLabel: MessageLabel):
        resp = rpc_check(self.puzzle.grid.encode())

        error = resp["error"]
        if error:
            msgLabel.configure(text=error)
            return None

        result = resp["result"]

        msgLabel.configure(text=result)

    def cue(self):
        self.puzzle.save_state()
        for row in self.puzzle.grid.rows:
            for cell in row:
                if cell.value != 0:
                    cell.given = True
        self.puzzle.draw_grid()

    def encode(self, openText):
        openText.delete("1.0", tk.END)
        openText.insert(tk.END, self.puzzle.grid.formatEncode())

    def fetch_strategies(self):
        resp = rpc_strategy()
        error = resp["error"]
        if error:
            return None

        result = resp["result"]
        result = json.loads(result)

        self.selected_strategy.set("Step")
        self.strategy_cb["values"] = result

    def image(self):
        self.puzzle.image()

    def input(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.decode(openText.get("1.0", tk.END))
        self.puzzle.grid.clearup()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def transpose(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.transpose()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def upDown(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.upDown()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def leftRight(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.leftRight()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def rotate(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.rotate()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def shuffle(self, openText):
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.grid.shuffle()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.init_history()

    def new(self):
        self.puzzle.grid.toZero()
        self.puzzle.draw_grid()
        self.puzzle.tipTool.tip_init()
        self.puzzle.turnoffTippedWidgets()
        self.puzzle.init_history()

    def output(self, openText):
        openText.delete("1.0", tk.END)
        openText.insert(tk.END, self.puzzle.grid.output())

    def solve(self, msgLabel: MessageLabel):
        resp = rpc_solve(self.puzzle.grid.encode())

        error = resp["error"]
        if error:
            msgLabel.configure(text=error)
            return None

        self.puzzle.turnoffTippedWidgets()
        self.puzzle.tipTool.tip_init()
        self.puzzle.save_state()
        result = resp["result"]

        self.puzzle.grid.decode(result)
        self.puzzle.grid.init()
        self.puzzle.grid.clearup()
        self.puzzle.draw_grid()

    def tip(self, msgLabel: MessageLabel):
        strategy = self.selected_strategy.get()
        self.puzzle.tipTool.tip(strategy, msgLabel)

    def open_draw_dialog(self):
        dialog = ChainDialog(self.master, "Draw chain and ...", AllCandColors)
        if not dialog.result:
            return
        shape, description, color = dialog.result

        if shape == "chain":
            chain = description.split(",")
            if len(chain) > 1:
                chain, factor = chain[0], float(chain[1])
            else:
                chain, factor = chain[0], 1
            rchain: RChain = analyzeChain(chain)
            if not rchain.rnodes:
                return
            self.puzzle.tipTool.drawChain(rchain)
        elif shape == "cand":
            pass
        elif shape == "value":
            pass
        elif shape == "cell":
            pass
        elif shape == "result":
            t = TipTool(self.puzzle)
            r = json.loads(description)

            rresult = unmarshal(r)
            if not rresult:
                return

            t.tip_block(rresult)
            t.tip_help_values(rresult)
            t.tip_help_chains(rresult)
            t.tip_help_cands(rresult)
            t.tip_help_chosen(rresult)
            t.tip_removed(rresult)

    def analyzeCells(self, cellDesc):
        cells = []
        pattern = r"R(\d)C(\d)"
        fs = re.fullmatch(pattern, cellDesc)
        if fs:
            f = fs.group(1, 2)
            cells.append(RCell(int(f[0]) - 1, int(f[1]) - 1))

            return cells

        pattern = r"R(\d)C(\d+)"
        fs = re.fullmatch(pattern, cellDesc)
        if fs:
            f = fs.group(1, 2)
            for colNum in sorted(f[1]):
                cells.append(RCell(int(f[0]) - 1, int(colNum) - 1))

            return cells

        pattern = r"R(\d+)C(\d)"
        fs = re.fullmatch(pattern, cellDesc)
        if fs:
            f = fs.group(1, 2)
            for rowNum in sorted(f[0]):
                cells.append(RCell(int(rowNum) - 1, int(f[1]) - 1))

            return cells

        pattern = r"B(\d)P(\d+)"
        fs = re.fullmatch(pattern, cellDesc)
        if fs:
            f = fs.group(1, 2)
            box = int(f[0])
            start_row = (box - 1) // 3 * 3
            start_col = (box - 1) % 3 * 3
            for pos in sorted(f[1]):
                row = start_row + (int(pos) - 1) // 3
                col = start_col + (int(pos) - 1) % 3
                cells.append(RCell(row, col))

            return cells

        return cells
